/*******************************************************************************
 * Copyright (c) 2010, 2017 西安秦晔信息科技有限公司
 * Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 *******************************************************************************/
package com.qinyeit.serviceapp.quartz;

import com.google.common.collect.ImmutableMap;
import com.qinyetech.springstage.core.lang.QyMap;
import com.qinyetech.springstage.core.utils.ClassScaner;
import lombok.extern.slf4j.Slf4j;
import org.quartz.*;
import org.quartz.impl.matchers.GroupMatcher;
import org.springframework.beans.annotation.AnnotationBeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.annotation.PostConstruct;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * <p>ClassName: com.qinyetech.webapp.quartz.QuartzTaskManager
 * <p>Function: 任务管理器
 * <p>date: 2018-09-08 13:02
 *
 * @author wuqing
 * @version 1.0
 * @since JDK 1.8
 */
@Component
@Slf4j
public class QuartzJobManager {
    @Autowired
    private Scheduler scheduler;

    /**
     * 添加任务，任务一旦添加会持久化到数据库中
     *
     * @param job
     */
    public void addJob(Class job) {
        try {
            Class tdCls = job.getClassLoader().loadClass(TriggerDetail.class.getName());
            if (!job.isAnnotationPresent(tdCls)) {
                return;
            }
            Annotation annotation = job.getAnnotation(tdCls);
            Map<String,String> triggerDetail =getTriggerDetail(annotation);
            String jobName=triggerDetail.get("jobName"),
                    jobGroup=triggerDetail.get("jobGroup"),
                    cronExpression=triggerDetail.get("cronExpression"),
                    description=triggerDetail.get("cronExpression");
            JobDetail jobDetail = JobBuilder.newJob(job)
                    .withIdentity(jobName, jobGroup)// 任务名称和组构成任务key
                    .build();

            // 定义调度触发规则
            // 使用cornTrigger规则
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity(jobName,jobGroup)// 触发器key
                    .withSchedule(CronScheduleBuilder.cronSchedule(cronExpression))
                    .withDescription(description)
                    .startNow()
                    .build();
            //传递给job对象的数据,在JobExecutionContext中可以获取到
            //注意： triggerDetail中的cron表达式可能会会被更改
            trigger.getJobDataMap().put("triggerDetail", triggerDetail);

            // 把作业和触发器注册到任务调度中
            scheduler.scheduleJob(jobDetail, trigger);
        } catch (ClassNotFoundException ex) {
            log.error("加载类失败", ex);
        } catch (Exception ex) {
            log.error("添加任务失败", ex);
        }
    }


    /**
     * 获取所有计划中的任务列表
     *
     * @return
     * @throws SchedulerException
     */
    public List<QyMap> getAllJobs() throws Exception {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
        List<QyMap> jobList = new ArrayList<QyMap>();
        for (JobKey jobKey : jobKeys) {
            jobList.add(getJob(jobKey.getName(), jobKey.getGroup()));
        }
        return jobList;
    }

    /**
     * 获取任务信息
     *
     * @param name
     * @param group
     * @return
     * @throws Exception
     */
    public QyMap getJob(String name, String group) throws Exception {
        //获取任务信息
        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(name, group));
        TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
        //获取trigger
        Trigger trigger = scheduler.getTrigger(triggerKey);

        Class jobDetailCls=jobDetail.getJobClass();
        Class tdCls = jobDetailCls.getClassLoader().loadClass(TriggerDetail.class.getName());
        Annotation annotation = jobDetailCls.getAnnotation(tdCls);
        Map<String,String> triggerDetail =getTriggerDetail(annotation);
        //实际使用的状态
        Trigger.TriggerState triggerState = scheduler.getTriggerState(trigger.getKey());
        String cronExpression = triggerDetail.get("cronExpression");
        if (trigger instanceof CronTrigger) {
            CronTrigger cronTrigger = (CronTrigger) trigger;
            //实际使用的触发器表达式
            cronExpression = cronTrigger.getCronExpression();
        }
        return new QyMap("jobName", triggerDetail.get("jobName"))
                .setv("jobGroup", triggerDetail.get("jobGroup"))
                .setv("description", triggerDetail.get("description"))
                .setv("cronExpression", cronExpression)
                .setv("jobStatus", triggerState);
    }


    /**
     * 所有正在运行的job
     *
     * @return
     * @throws SchedulerException
     */
    public List<QyMap> getRunningJob() throws Exception {
        List<JobExecutionContext> executingJobs = scheduler.getCurrentlyExecutingJobs();
        List<QyMap> jobList = new ArrayList<QyMap>(executingJobs.size());
        for (JobExecutionContext executingJob : executingJobs) {
            JobKey key = executingJob.getJobDetail().getKey();
            jobList.add(getJob(key.getName(), key.getGroup()));
        }
        return jobList;
    }

    /**
     * 暂停一个job
     *
     * @throws SchedulerException
     */
    public void pauseJob(String name, String group) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(name, group);
        scheduler.pauseJob(jobKey);
    }

    /**
     * 恢复一个job
     *
     * @throws SchedulerException
     */
    public void resumeJob(String name, String group) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(name, group);
        scheduler.resumeJob(jobKey);
    }


    /**
     * 删除一个job
     *
     * @throws SchedulerException
     */
    public void deleteJob(String name, String group) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(name, group);
        scheduler.deleteJob(jobKey);
    }

    /**
     * 立即执行job
     *
     * @throws SchedulerException
     */
    public void runJobNow(String name, String group) throws SchedulerException {
        JobKey jobKey = JobKey.jobKey(name, group);
        scheduler.triggerJob(jobKey);
    }

    /**
     * 更新job时间表达式
     *
     * @throws SchedulerException
     */
    public void updateJobCron(String name, String group, String expression) throws SchedulerException {
        log.debug(">>>>>>>>>cron表达式更新为>>>>>>>>>>>>>>>>{}", expression);
        TriggerKey triggerKey = TriggerKey.triggerKey(name, group);
        //从scheduler 中获取Trigger
        CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
        CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(expression);
        trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
        scheduler.rescheduleJob(triggerKey, trigger);

    }

    /**
     * 添加过的任务会持久化到数据库中，应用中初始的任务只需要初始化一次。
     * 以后再添加任务可以通过调用 addJob 来添加
     */
    @PostConstruct
    public void initialize() {
        GroupMatcher<JobKey> matcher = GroupMatcher.anyJobGroup();
        try {
            Set<JobKey> jobKeys = scheduler.getJobKeys(matcher);
            if (CollectionUtils.isEmpty(jobKeys)) {
                log.debug("初始化所有定时任务.....");
                //扫描包下所有的job
                Set<Class> jobClass = ClassScaner.scan("com.qinyeit.serviceapp.quartz.job", TriggerDetail.class);
                if (CollectionUtils.isEmpty(jobClass)) {
                    return;
                }
                jobClass.forEach(this::addJob);
            }
        } catch (SchedulerException e) {
            log.error("初始化scheduler 任务失败！", e);
        }
    }

    private Map<String, String> getTriggerDetail(Annotation triggerDetailAnnotation) {

        return ImmutableMap.of("jobName",AnnotationUtils.getValue(triggerDetailAnnotation,"jobName").toString(),
                "jobGroup", AnnotationUtils.getValue(triggerDetailAnnotation,"jobGroup").toString(),
                "cronExpression", AnnotationUtils.getValue(triggerDetailAnnotation,"cronExpression").toString(),
                "description", AnnotationUtils.getValue(triggerDetailAnnotation,"description").toString());
    }


}
